/* ************************************************************************ */
/*                                                                          */
/*  kyototycoon-neko														*/
/*  HaXe/neko Library													    */
/*  Copyright (c)2011 François Nicaise                                      */
/*                                                                          */
/*  This program is free software; you can redistribute it and/or modify    */
/*  it under the terms of the GNU General Public License as published by    */
/*  the Free Software Foundation; either version 2 of the License, or       */
/*  (at your option) any later version.                                     */
/*                                                                          */
/*  This program is distributed in the hope that it will be useful,         */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           */
/*  GNU General Public License for more details.                            */
/*                                                                          */
/*  You should have received a copy of the GNU General Public License       */
/*  along with this program; if not, write to the Free Software             */
/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*                                                                          */
/* ************************************************************************ */

/* NOTE :
	you can found a C++ documentation either on the internet
	http://fallabs.com/kyototycoon/api/classkyototycoon_1_1RemoteDB.html
	or on /usr/local/include/ktremotedb.h (Linux)
*/

#define IMPLEMENT_API
#include <ktremotedb.h>
#include <hx/CFFI.h>


DEFINE_KIND(k_connection);
DEFINE_KIND(k_cursor);

using namespace std;
using namespace kyototycoon;

#define KTREMOTEDB(o) ((RemoteDB* )val_data(o))
#define KTCURSOR(o) ((RemoteDB::Cursor* )val_data(o))

static void kt_free_connection( value o ) {
	RemoteDB* db = KTREMOTEDB(o);
	delete db;
}

static value kt_open( value host, value port, value timeout ) {
	RemoteDB* db = new RemoteDB();

	value v = alloc_abstract(k_connection,db);
	val_gc(v,kt_free_connection);

	if (!db->open( val_string(host), val_int(port), val_float(timeout ) )) {
		buffer b = alloc_buffer(db->error().message());
		bfailure(b);
	}

	return v;
}

static value kt_close( value o ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	bool success = db->close();

	kt_free_connection(o);	
	val_gc(o,NULL);

	return success ? val_true : val_false;
}

  /**
   * Remove all records.
   * @return true on success, or false on failure.
   */
static value kt_clear( value o ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	bool success = db->clear();

	return success ? val_true : val_false;
}

  /**
   * Synchronize updated contents with the file and the device.
   */
static value kt_synchronize( value o, value hard ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);

	val_is_bool(hard);	
	bool success = db->synchronize(val_bool(hard));

	return success ? val_true : val_false;
}

  /**
   * Get the number of records.
   */
static value kt_count( value o ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	return alloc_int( db->count() );
}

  /**
   * Get the size of the database file.
   */
static value kt_size( value o ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	return alloc_int( db->size() );
}

  /**
   * Set the value of a record.
   * @return true on success, or false on failure.
   */
static value kt_set( value o, value k, value v, value xt ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	bool success = db->set( val_string(k), val_string(v) );

	int time = val_int(xt);
	if( time != 0 )
		success = db->set( val_string(k), val_string(v), time  );
	else
		success = db->set( val_string(k), val_string(v) );

	return success ? val_true : val_false;
}

  /**
   * Set the value of a record.
   * @return true on success, or false on failure.
   */
static value kt_add( value o, value k, value v, value xt ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	bool success =false;
	int time = val_int(xt);
	if( time != 0 )
		success = db->add( val_string(k), val_string(v), time  );
	else
		success = db->add( val_string(k), val_string(v) );

	return success ? val_true : val_false;
}

  /**
   * Replace the value of a record.
   * @return true on success, or false on failure.
   */
static value kt_replace( value o, value k, value v, value xt ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	bool success =false;
	int time = val_int(xt);
	if( time != 0 )
		success = db->replace( val_string(k), val_string(v), time  );
	else
		success = db->replace( val_string(k), val_string(v) );

	return success ? val_true : val_false;
}

  /**
   * Set the value of a record.
   * @return true on success, or false on failure.
   */
static value kt_append( value o, value k, value v, value xt ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	bool success =false;
	int time = val_int(xt);
	if( time != 0 )
		success = db->append( val_string(k), val_string(v), time  );
	else
		success = db->append( val_string(k), val_string(v) );

	return success ? val_true : val_false;
}

  /**
   * Add a number to the numeric integer value of a record.
   * Note : record MUST be created using this method
   * @return updated value.
   */
static value kt_increment( value o, value args ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	int ret = 0;

	enum { akey, avalue, aorig, anegInf, aposInf, axt, asize };
	int l = val_array_size(args);
	value * infos = val_array_value( args );
	if( l != asize ) return val_null; 

	bool neg = val_bool(infos[anegInf]);
	bool pos = val_bool(infos[aposInf]);
	int64_t vorig = val_int(infos[aorig]);
	std::string k = val_string(infos[akey]);
	int num = val_int(infos[avalue]);
	int vxt = val_int(infos[axt]);
	int time = vxt == 0 ? kc::INT64MAX : vxt;
	if( neg ) {
		ret = db->increment( k, num, kc::INT64MIN, time  );
	}
	else if( pos ) {
		ret = db->increment( k, num, kc::INT64MAX, time  );
	}
	else {	
		if( vorig == 0 && vxt == 0 )
			ret = db->increment( k, num );
		else if( vxt != 0 )
			ret = db->increment( k, num, vorig, time  );
		else
			ret = db->increment( k, num, vorig );
	}

	return alloc_int( ret );
}

  /**
   * Add a number to the numeric double value of a record.
   * Note : record MUST be created using this method
   * @return updated value.
   */
static value kt_increment_double( value o, value args ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	
	double ret = 0.0;

	enum { akey, avalue, aorig, anegInf, aposInf, axt, asize };
	int l = val_array_size(args);
	value * infos = val_array_value( args );
	if( l != asize ) return val_null; 

	bool neg = val_bool(infos[anegInf]);
	bool pos = val_bool(infos[aposInf]);
	double vorig = (double)val_float(infos[aorig]);
	std::string k = val_string(infos[akey]);
	double num = (double)val_float(infos[avalue]);
	int vxt = val_int(infos[axt]);
	int time = vxt == 0 ? kc::INT64MAX : vxt;
	if( neg ) 
		ret = db->increment_double( k, num, kc::INT64MIN, time  );
	else if( pos )
		ret = db->increment_double( k, num, kc::INT64MAX, time  );
	else {
		if( vorig == 0.0 && vxt == 0 )
			ret = db->increment_double( k, num );
		else if( vxt != 0 ) 
			ret = db->increment_double( k, num, vorig, time  );
		else
			ret = db->increment_double( k, num, vorig );
	}
	
	return alloc_float( ret );
}

  /**
   * Perform compare-and-swap.
   * @return true on success, or false on failure.
   */
static value kt_cas( value o, value k, value ov, value nv, value xt ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	bool success =false;
	int time = val_int(xt);
	if( time != 0 )
		success = db->cas( val_string(k), val_string(ov), val_string(nv), time  );
	else
		success = db->cas( val_string(k), val_string(ov), val_string(nv) );

	return success ? val_true : val_false;
}

  /**
   * Remove a record.
   * @return true on success, or false on failure.
   */
static value kt_remove( value o, value k ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	bool success =false;
	success = db->remove( val_string(k) );
	return success ? val_true : val_false;
}

  /**
   * Retrieve the value of a record.
   */
static value kt_get( value o, value k ) {
	val_check_kind(o,k_connection);

	RemoteDB* db = KTREMOTEDB(o);
	std::string result;
	if( !db->get( val_string(k), &result ) )
		return val_null;

	value r;
	r = alloc_string( result.c_str() );
	return r;
}

  /**
   * Scan the database and eliminate regions of expired records.
   * @param step the number of steps.  If it is not more than 0, the whole region is scanned.
   * @return true on success, or false on failure.
   */
static value kt_vacuum( value o, value step ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	bool success;
	success = db->vacuum( val_int( step ) );
	return success ? val_true : val_false;
}

  /**
   * Get keys matching a prefix string.
   * @param prefix the prefix string.
   * @param max the maximum number to retrieve.  If it is negative, no limit is specified.
   * @return array or null on failure.
   */
static value kt_match_prefix( value o , value prefix, value max ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	vector<string> results;
	int64_t n = db->match_prefix( val_string( prefix ), &results, val_int( max ) );
	if( n <= 0 ) {
		return val_null;
	}
	value nresults;
	nresults = alloc_array(n);
	int i;
	for(i=0;i<n;i++) {
		val_array_set_i( nresults, i, alloc_string( results.at(i).c_str() ) );
	}
	return nresults;	
}

  /**
   * Get keys matching a regular expression string.
   * @param regex the regular expression string.
   * @param max the maximum number to retrieve.  If it is negative, no limit is specified.
   * @return array or null on failure.
   */
static value kt_match_regex( value o, value regex, value max ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	vector<string> results;
	int64_t n = db->match_regex( val_string( regex ), &results, val_int( max ) );
	if( n <= 0 ) {
		return val_null;
	}
	value nresults;
	nresults = alloc_array(n);
	int i;
	for(i=0;i<n;i++) {
		val_array_set_i( nresults, i, alloc_string( results.at(i).c_str() ) );
	}
	return nresults;	
}

  /**
   * Set the target database.
   */
static void kt_set_target( value o, value dbname ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	db->set_target( val_string( dbname ) );
}

  /**
   * Get the expression of the socket.
   */
static value kt_expression( value o ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	return alloc_string( db->expression().c_str() );
}

  /**
   * Get the miscellaneous status information.
   */
static value kt_status( value o ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	map<string, string> results;
	if( !db->status( &results ) )
		return val_null;

	value nresults;

	int n = (int)results.size();
	nresults = alloc_array(n);
	
	int i = 0;
	map<string,string>::iterator it;
	for ( it=results.begin() ; it != results.end(); it++ ) {
		value nDesc = alloc_array(2);
		string key = (*it).first;
		string value = (*it).second;
		val_array_set_i( nDesc, 0, alloc_string( key.c_str() ) );  
		val_array_set_i( nDesc, 1, alloc_string( value.c_str() ) );  
		val_array_set_i( nresults, i++, nDesc );
	}
	
	return nresults;	
}

  /**
   * Store records at once.
   * @param recs the records to store.
   * @param xt the expiration time from now in seconds.  If it is negative, the absolute value
   * is treated as the epoch time.
   * @param atomic true to perform all operations atomically, or false for non-atomic operations.
   * @return the number of stored records, or -1 on failure.
   */
static value kt_set_bulk( value o, value recs, value xt, value atomic ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);

	map<string, string> out;
	int l = val_array_size(recs);	
	value * infos = val_array_value( recs );
	int i;
	for(i = 0; i<l; i++) {
		value * desc = val_array_value( infos[i] );
		if( desc != NULL )
			out.insert( pair<string,string>( val_string( desc[0] ), val_string( desc[1] ) ) );
	}

	int time = val_int(xt);
	int64_t n;
	if( time != 0 )
		n = db->set_bulk( out, time , val_bool( atomic ) );
	else
		n = db->set_bulk( out, val_bool( atomic ) );

	if( n <= 0 ) {
		return alloc_int( -1 );
	}

	return alloc_int( n );
}

  /**
   * Remove records at once.
   * @param keys the keys of the records to remove.
   * @param atomic true to perform all operations atomically, or false for non-atomic operations.
   * @return the number of removed records, or -1 on failure.
   */
static value kt_remove_bulk( value o, value keys, value atomic ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);

	vector<string> out;
	value * vkeys = val_array_value( keys );
	int l = val_array_size(keys);
	int i;
	for(i = 0; i<l; i++) {
		out.push_back( val_string( vkeys[ i ] ) );
	}
	int64_t n = db->remove_bulk( out, val_bool( atomic ) );
	if( n <= 0 ) {
		return alloc_int( -1 );
	}

	return alloc_int( n );
}

static value kt_get_bulk( value o, value keys, value atomic ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);

	vector<string> out;
	value * vkeys = val_array_value( keys );
	int l = val_array_size(keys);
	int i;
	for(i = 0; i < l; i++) {
		out.push_back( val_string( vkeys[ i ] ) );
	}
	map<string, string> results;
	int64_t n = db->get_bulk( out, &results, val_bool( atomic ) );
	if( n <= 0 ) 
		return val_null;

	value nresults;
	nresults = alloc_array((int)n);

	i = 0;
	map<string,string>::iterator it;
	for ( it=results.begin() ; it != results.end(); it++ ) {
		value nDesc = alloc_array(2);
		string key = (*it).first;
		string value = (*it).second;
		val_array_set_i( nDesc, 0, alloc_string( key.c_str() ) );  
		val_array_set_i( nDesc, 1, alloc_string( value.c_str() ) );  
		val_array_set_i( nresults, i++, nDesc );
	}
	
	return nresults;	
}

  /**
   * Store records at once in the binary protocol.
   * This implementation does not take care of BulkRecord's xt param or opts
   * @param recs the records to store.
   * @return the number of stored records, or -1 on failure.
   */
static value kt_set_bulk_binary( value o, value recs ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);

	vector<RemoteDB::BulkRecord> out;
	value * infos = val_array_value( recs );
	int l = val_array_size(recs);
	int i;
	for(i = 0; i < l; i++) {
		value * desc = val_array_value( infos[i] );

		if( desc != NULL ) {
			if( desc[0] != NULL && desc[1] != NULL && desc[2] != NULL && desc[3] != NULL ) {
				RemoteDB::BulkRecord br;
				br.key = val_string( desc[0] );
//				cout << "set:" << br.key + "\n";
				br.value = val_string( desc[1] );
				int xt = val_int( desc[2] );
				br.xt = xt != 0 ? xt : kc::INT64MAX;
//				cout << br.xt << "\n";
				br.dbidx = desc[3] != 0 ? val_int(desc[3]) : 0;
				out.push_back( br );
			}
		}
	}

	int64_t n;
	n = db->set_bulk_binary( out );

	if( n <= 0 ) {
		return alloc_int( -1 );
	}

	return alloc_int( n );
}


  /**
   * Retrieve records at once in the binary protocol.
   * @param recs the records to retrieve. 
   * @return the records or null on failure
   */
static value kt_get_bulk_binary( value o, value keys, value dbidx ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);

	if (val_is_null(keys))
      return val_null;

	vector<RemoteDB::BulkRecord> out;
	int l = val_array_size(keys);
	value * vkeys = val_array_value( keys );
	int i;
	for(i = 0; i < l; i++) {
		if( vkeys[i] != NULL ) {
			RemoteDB::BulkRecord br;
			br.dbidx = val_int( dbidx );
			br.key = val_string( vkeys[ i ] );
//			cout << "looking for :" << br.key + "\n";
			out.push_back( br );
		}
	}
	
	int64_t n = db->get_bulk_binary( &out );
	if( n <= 0 ) {
//		cout << "nothing found !\n";
		return val_null;
	}
	
	//cout << "found=" << n << "\n";
	value nresults;
	nresults = alloc_array((int)(n));
	i = 0;
	for( i; i<n; i++ ) {
		RemoteDB::BulkRecord br = out.at(i);
		value nDesc = alloc_array(4);
		string key = br.key;
		string value = br.value;
		//cout << key << "=>" << value << "\n";
		val_array_set_i( nDesc, 0, alloc_string( key.c_str() ) );  
		val_array_set_i( nDesc, 1, alloc_string( value.c_str() ) );
		std::stringstream xt;
		xt << br.xt;
		val_array_set_i( nDesc, 2, alloc_string( xt.str().c_str() ) );
		std::stringstream dbidx;
		dbidx << br.dbidx;
		val_array_set_i( nDesc, 3, alloc_string( dbidx.str().c_str() ) );  
		val_array_set_i( nresults, i, nDesc );
	}
	
	return nresults;	
}

  /**
   * Remove records at once.
   * @param keys the keys of the records to remove.
   * @param atomic true to perform all operations atomically, or false for non-atomic operations.
   * @return the number of removed records, or -1 on failure.
   */
static value kt_remove_bulk_binary( value o, value keys ) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);


	if (val_is_null(keys))
      return val_null;

	vector<RemoteDB::BulkRecord> out;
	int l = val_array_size(keys);
	value * vkeys = val_array_value( keys );
	int i;
	for(i = 0; i < l; i++) {
		if( vkeys[i] != NULL ) {
			RemoteDB::BulkRecord br;
			br.dbidx = 0;
			br.key = val_string( vkeys[ i ] );
			out.push_back( br );
		}
	}
	
	int64_t n = db->remove_bulk_binary( out );
	if( n <= 0 ) {
		return alloc_int( -1 );
	}

	return alloc_int( n );
}

static void kt_destroy_cursor( value c ) {
	RemoteDB::Cursor* cursor = KTCURSOR(c);
	delete cursor;
}

static void kt_delete_cursor( value c ) {
	val_check_kind(c,k_cursor);
	kt_destroy_cursor( c );
	val_gc(c,NULL);
}

/**
* Create a cursor object.
* @return the return value is the created cursor object.
*/
static value kt_cursor(value o) {
	val_check_kind(o,k_connection);
	RemoteDB* db = KTREMOTEDB(o);
	RemoteDB::Cursor * cursor = db->cursor();
	value v = alloc_abstract(k_cursor,cursor);
	val_gc(v,kt_destroy_cursor);
	return v;
}

/**
 * Jump the cursor to the first record for forward scan.
 * @return true on success, or false on failure.
 */
static value kt_cursor_jump( value c ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success = cur->jump();
	return success ? val_true : val_false;
}

/**
 * Jump the cursor to a record for forward scan.
 * @return true on success, or false on failure.
 */
static value kt_cursor_jump_tokey( value c, value key ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success = cur->jump( val_string( key ));
	return success ? val_true : val_false;
}


/**
 * Jump the cursor to the last record for backward scan.
 * @return true on success, or false on failure.
 * @note This method is dedicated to tree databases.  Some database types, especially hash
 * databases, may provide a dummy implementation.
 */
static value kt_cursor_jump_back( value c ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success = cur->jump_back();
	return success ? val_true : val_false;
}


/**
 * Remove the current record.
 * @return true on success, or false on failure.
 * @note If no record corresponds to the key, false is returned.  The cursor is moved to the
 * next record implicitly.
 */
static value kt_cursor_remove( value c ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success = cur->remove();
	return success ? val_true : val_false;
}

/**
 * Step the cursor to the next record.
 * @return true on success, or false on failure.
 */
static value kt_cursor_step( value c ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success = cur->step();
	return success ? val_true : val_false;
}

/**
 * Step the cursor to the previous record.
 * @return true on success, or false on failure.
 */
static value kt_cursor_step_back( value c ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success = cur->step_back();
	return success ? val_true : val_false;
}

/**
 * Get the key of the current record.
 * @param step true to move the cursor to the next record, or false for no move.
 */
static value kt_cursor_get_key( value c, value step ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	std::string s;
	bool success = cur->get_key(&s, val_bool(step));
	return success ? alloc_string( s.c_str() ) : val_null;
}

/**
 * Get the value of the current record.
 * @param step true to move the cursor to the next record, or false for no move.
 */
static value kt_cursor_get_value( value c, value step ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	std::string s;
	bool success = cur->get_value(&s, val_bool(step));
	return success ? alloc_string( s.c_str() ) : val_null;
}

/**
 * Set the value of the current record.
 * @param value the new value
 * @param xt the expiration time from now in seconds.  If it is negative, the absolute value
 * is treated as the epoch time.
 * @param step true to move the cursor to the next record, or false for no move.
 */
static value kt_cursor_set_value( value c, value v, value xt, value step ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success = cur->set_value_str(val_string(v), val_int(xt), val_bool(step));
	return success ? val_true : val_false;
}

/**
 * Get a pair of the key and the value of the current record.
 * @param step true to move the cursor to the next record, or false for no move.
 */
static value kt_cursor_get( value c, value step ) {
	val_check_kind(c,k_cursor);
	RemoteDB::Cursor* cur = KTCURSOR(c);
	bool success;
	value rs;
	string key;
	string value;
	success = cur->get( &key, &value, NULL, val_bool(step));
	if( !success ) 
		return val_null;

	rs = alloc_array(2);
	val_array_set_i( rs, 0, alloc_string( key.c_str() ) );  
	val_array_set_i( rs, 1, alloc_string( value.c_str() ) );  
	return rs;
}



/*--------------------------------------------------------------------------*/


DEFINE_PRIM(kt_open,3)
DEFINE_PRIM(kt_close,1)
DEFINE_PRIM(kt_clear,1)
DEFINE_PRIM(kt_synchronize,2)
DEFINE_PRIM(kt_count,1)
DEFINE_PRIM(kt_size,1)
DEFINE_PRIM(kt_set,4)
DEFINE_PRIM(kt_add,4)
DEFINE_PRIM(kt_replace,4)
DEFINE_PRIM(kt_append,4)
DEFINE_PRIM(kt_increment,2)
DEFINE_PRIM(kt_increment_double,2)
DEFINE_PRIM(kt_cas,5)
DEFINE_PRIM(kt_remove,2)
DEFINE_PRIM(kt_get,2)
DEFINE_PRIM(kt_vacuum,2)
DEFINE_PRIM(kt_match_prefix,3)
DEFINE_PRIM(kt_match_regex,3)
DEFINE_PRIM(kt_set_target,2)
DEFINE_PRIM(kt_expression,1)
DEFINE_PRIM(kt_status,1)
DEFINE_PRIM(kt_set_bulk,4)
DEFINE_PRIM(kt_remove_bulk,3)
DEFINE_PRIM(kt_get_bulk,3)
DEFINE_PRIM(kt_get_bulk_binary,3)
DEFINE_PRIM(kt_remove_bulk_binary,2)
DEFINE_PRIM(kt_set_bulk_binary,2)
DEFINE_PRIM(kt_cursor,1)
DEFINE_PRIM(kt_delete_cursor,1)
DEFINE_PRIM(kt_cursor_jump,1)
DEFINE_PRIM(kt_cursor_jump_back,1)
DEFINE_PRIM(kt_cursor_jump_tokey,2)
DEFINE_PRIM(kt_cursor_remove,1)
DEFINE_PRIM(kt_cursor_step,1)
DEFINE_PRIM(kt_cursor_step_back,1)
DEFINE_PRIM(kt_cursor_get_key,2)
DEFINE_PRIM(kt_cursor_get_value,2)
DEFINE_PRIM(kt_cursor_set_value,4)
DEFINE_PRIM(kt_cursor_get,2)
